# component.py

from __future__ import division

from ot_simulator.template import param_funs

class component(object):
    r'''Base class for all components.
    '''

    #: default, overridden in base classes
    input_parameters = ()

    def __init__(self, **kws):
        for name, value in kws.iteritems():
            if not name.startswith('_') and name != 'name' \
               and name not in self.input_parameters:
                raise AttributeError("{0}: {1} invalid parameter name"
                                       .format(self.__class__.__name__, name))
            setattr(self, name, value)
        if not hasattr(self, 'name'): self.name = self.__class__.__name__
        print "name is", self.name

    def register(self, params):
        params[self.name] = self

    def do_debug(self, params, name):
        r'''
            Looks for a 'debug' parameter with either ``name`` as its value,
            or a tuple containing ``name``.

            >>> c = component()
            >>> c.do_debug({}, 'foobar')
            False
            >>> c = component(debug='foobar')
            >>> c.do_debug({}, 'foobar')
            True
            >>> c.do_debug({}, 'foo')
            False
            >>> c = component(debug=('foobar', 'foo'))
            >>> c.do_debug({}, 'foobar')
            True
            >>> c.do_debug({}, 'foo')
            True
            >>> c.do_debug({}, 'bar')
            False
        '''
        debug = self._get_param(params, 'debug', ())
        if isinstance(debug, str): return name == debug
        return name in debug

    def get_param(self, params, name, default = param_funs.No_default):
        r'''
            Gets parameter ``name`` for this component.

            The ``name`` is searched for in the following places (in this
            order):

                - (self.name, ``name``) as a key in ``params``
                - self.``name``
                - ``name`` as a key in ``params``
            
            >>> c = component(name='comp_name', foo=22)
            >>> c.get_param({('comp_name', 'foo'): 44, 'foo': 55}, 'foo')
            44
            >>> c.get_param({'foo': 55}, 'foo')
            22
            >>> c.get_param({'foo2': 55}, 'foo2')
            55
            >>> c.get_param({'foo2': 55}, 'foo3')
            Traceback (most recent call last):
                ...
            KeyError: "comp_name can not find param: 'foo3'"

            If the value is a function, it is called passing ``params`` as
            the only parameter.  Its return value is the parameter
            value:

            >>> c.get_param({'foo2': lambda params: params['bar'],
            ...              'bar': 66},
            ...             'foo2')
            66

            If ``default`` is specified, use it rather than generating a
            KeyError exception.  The ``default`` may be a function.

            >>> c.get_param({'foo2': 55}, 'foo3', 77)
            77
        '''
        ans = self._get_param(params, name, default)
        if self.do_debug(params, 'get_param'):
            print "%s.get_param(%s):" % (self.name, name), ans
        return ans

    def _get_param(self, params, name, default = param_funs.No_default):
        return param_funs.get_value(self.get_raw_param(params, name, default),
                                    params)

    def get_raw_param(self, params, name, default = param_funs.No_default):
        if (self.name, name) in params:
            return param_funs.get_param(params, (self.name, name))
        if hasattr(self, name): return getattr(self, name)
        if name in params: return param_funs.get_param(params, name)
        if default is not param_funs.No_default: return default
        raise KeyError("%s can not find param: %r" % (self.name, name))

    def set_param(self, params, name, value):
        r'''
            Sets ``name`` parameter for this component in ``params``.

            >>> c = component(name='comp_name')
            >>> params = {}
            >>> c.set_param(params, 'foo', 33)
            >>> params
            {('comp_name', 'foo'): 33}
        '''
        params[self.name, name] = value
        if self.do_debug(params, 'set_param'):
            print "%s.set_param(%s, %r)" % (self.name, name, value)


def test():
    import doctest
    import sys
    sys.exit(doctest.testmod()[0])

if __name__ == "__main__":
    test()
